package com.sophie.dao;

import com.sophie.util.DruidTool;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 编写工具类：通用增，删，改，查操作
 */
public class BaseDAO {
    /**
     *
     * @param sql  insert语句,update语句,delete语句
     * @param params  sql语句中问号的值
     * @return   返回影响的行数
     * @throws SQLException
     */
    public int update(String sql,Object... params) throws SQLException{
        //步骤1：获取连接对象
        Connection conn = DruidTool.getConnection();
        //步骤2：创建PreparedStatement
        PreparedStatement ps = conn.prepareStatement(sql);
        //步骤3：给sql语句中问号赋值 ps.setXXX(问号的位置,值);
        if(params!=null && params.length>0){
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1, params[i]);
            }
        }
        //步骤4：调用执行sql语句的方法
        int n = ps.executeUpdate();
        //步骤5：释放资源
        DruidTool.closeConn(ps,conn);
        return n;
    }

    /**
     * 使用事务以后的通用的增删改操作（version 2.0）
     * @param conn
     * @param sql
     * @param params
     */
    public int update(Connection conn, String sql, Object... params) throws SQLException{
        // 1.获取PreparedStatement的实例 (或：预编译sql语句)
        PreparedStatement ps  = conn.prepareStatement(sql);
        // 2.填充占位符
        if(params!=null && params.length>0){
            //遍历数组,数组索引从0开始
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1,params[i]);
            }
        }
        // 3.执行sql语句
        int n = ps.executeUpdate();
        // 一定不要关闭connection
        // 关闭了就不是同一个事务
        return n;
    }

    /**
     * 用于auto-generated,只适用于insert语句
     * 执行insert语句返回自动递增ID值
     * 主键是int类型，并且设置auto_increment
     * @param sql insert语句
     * @param params inserty语句中问号的值
     * @return  ID值
     * @throws SQLException
     */
    public Integer insertReturnKey(String sql,Object ... params)
            throws SQLException {
        //返回的ID值
        Integer id=null;
        //1.获取Connection对象
        Connection conn =DruidTool.getConnection();
        //2.创建PreparedStatement对象(预编译处理对象)
        PreparedStatement ps=conn.prepareStatement(sql,
                Statement.RETURN_GENERATED_KEYS);
        //3.给sql语句中问号赋值
        if(params!=null && params.length>0){
            //遍历数组,数组索引从0开始
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1,params[i]);
            }
        }
        //4.执行sql语句
        int  n = ps.executeUpdate();
        if(n>0){
            //插入成功
            ResultSet rs = ps.getGeneratedKeys(); // ^_^ 获取id值
            if(rs.next()){
                id = rs.getInt(1); //取出id值
            }
        }
        //5.释放资源
        DruidTool.closeConn(ps,conn);
        return  id;
    }


    /**
     * 通用的针对于不同表的查询:返回一个对象
     * @param sql 查询sql语句
     * @param c  查询的结果对应的实体类的Class对象
     * @param params sql语句中问号的值
     * @return  这条select语句返回的对象
     * @param <T> 泛型方法
     * @throws SQLException
     */
    public <T> T getOne(String sql,Class<T> c,Object... params) throws SQLException{
        //1.返回的对象
        T obj=null;
        //2.获取连接对象
        Connection conn =DruidTool.getConnection();
        //3.获取预编译sql语句对象
        PreparedStatement ps = conn.prepareStatement(sql);
        //4.填充占位符 ?
        if(params!=null && params.length>0){
            //遍历params
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1,params[i]);
            }
        }
        //5.执行查询语句,得到结果集
        ResultSet rs = ps.executeQuery();
        //6.处理结果集
        //6.1 得到结果集的元数据
        ResultSetMetaData metaData = rs.getMetaData();
        //6.2 获取结果集中总列数
        int columnCount = metaData.getColumnCount();
        //6.3 调用next()方法，将光标往下移动
        if(rs.next()){
            try {
                //6.4利用反射创建这个T对象的对象
                obj= c.getConstructor().newInstance();
                //6.5遍历每一列
                for (int i = 1; i <=columnCount; i++) {
                     //<1> 获取每一列的值
                    Object columnValue = rs.getObject(i);
                    //<2> 获取列名
                    String columnLabel = metaData.getColumnLabel(i);
                    //<3> 利用反射找到这个Field
                    Field field = c.getDeclaredField(columnLabel);
                    //暴力访问私有属性
                    field.setAccessible(true);
                    //给属性赋值
                    field.set(obj, columnValue);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        //7.释放资源
        DruidTool.closeConn(rs,ps,conn);
        //8.记得返回obj对象
        return obj;
    }


    /**
     * 通用的针对于不同表的查询:List集合，存储多个对象
     * @param sql 查询sql语句
     * @param c  查询的结果对应的实体类的Class对象
     * @param params sql语句中问号的值
     * @return  list集合
     * @param <T> 泛型方法
     * @throws SQLException
     */
    public <T> List<T> getAll(String sql,Class<T> c,Object... params) throws SQLException{
        //1.创建一个空列表对象
        List<T> list=new ArrayList<>();
        //2.获取连接对象
        Connection conn =DruidTool.getConnection();
        //3.获取预编译sql语句对象
        PreparedStatement ps = conn.prepareStatement(sql);
        //4.填充占位符 ?
        if(params!=null && params.length>0){
            //遍历params
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1,params[i]);
            }
        }
        //5.执行查询语句,得到结果集
        ResultSet rs = ps.executeQuery();
        //6.处理结果集
        //6.1 得到结果集的元数据
        ResultSetMetaData metaData = rs.getMetaData();
        //6.2 获取结果集中总列数
        int columnCount = metaData.getColumnCount();
        //6.3 调用next()方法，将光标往下移动
        while(rs.next()){
            try {
                //6.4利用反射创建这个T对象的对象
                T obj= c.getConstructor().newInstance();
                //6.5遍历每一列
                for (int i = 1; i <=columnCount; i++) {
                    //<1> 获取每一列的值
                    Object columnValue = rs.getObject(i);
                    //<2> 获取列名
                    String columnLabel = metaData.getColumnLabel(i);
                    //<3> 利用反射找到这个Field
                    Field field = c.getDeclaredField(columnLabel);
                    //暴力访问私有属性
                    field.setAccessible(true);
                    //给属性赋值
                    field.set(obj, columnValue);
                }

                //6.6装入到list集合中
                list.add(obj);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        //7.释放资源
        DruidTool.closeConn(rs,ps,conn);
        //8.记得返回obj对象
        return list;
    }


    /**
     * 通用的获取只有一行一列的Long类型的方法 (获取的是单个值)
     */
    public Long getScalar(String sql, Object... args) throws SQLException {
        Long value = 0L;
        //1.获取连接对象
        Connection conn = DruidTool.getConnection();
        //2.创建预编译sql语句对象
        PreparedStatement ps = conn.prepareStatement(sql);
        //3.给sql语句赋值
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
        }
        //4.执行查询
        ResultSet rs = ps.executeQuery();
        //5.处理结果
        if (rs.next()) {
            value = rs.getLong(1);
        }
        //6.释放资源
        DruidTool.closeConn(rs,ps,conn);
        return value;//记得返回这个结果
    }

    /**
     * 通用分页查询方法
     * @param limitSql
     * @param c
     * @param args
     * @return
     * @param <T>
     * @throws SQLException
     */
    public <T> List<T> page(String limitSql,Class<T> c, Object... args)
            throws SQLException{
        //任务2.每页查到的记录存放的集合,通过查询数据库得到
        List<T> list = new ArrayList<>();
        Connection conn = DruidTool.getConnection();
        PreparedStatement ps = conn.prepareStatement(limitSql);
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
        }
        ResultSet rs = ps.executeQuery();
        //得到结果集的元数据：ResultSetMetaData
        ResultSetMetaData metaData = rs.getMetaData();
        // 通过ResultSetMetaData得到columnCount,columnLabel；通过ResultSet得到列值
        int columnCount = metaData.getColumnCount();
        while (rs.next()) {
            try {
                // 利用反射创建对象
                T obj = c.getConstructor().newInstance();
                // 遍历每一个列
                for (int i = 1; i <= columnCount; i++) {
                    // 获取列值
                    Object columnVal = rs.getObject(i);
                    // 获取列的别名:列的别名，使用类的属性名充当
                    String columnLabel = metaData.getColumnLabel(i);
                    // 使用反射，给对象的相应属性赋值
                    Field field = c.getDeclaredField(columnLabel);
                    //暴力访问
                    field.setAccessible(true);
                    //给属性设置
                    field.set(obj, columnVal);
                }
                //把对象装入集合
                list.add(obj);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        DruidTool.closeConn(conn);
        return list;//任务5：返回这个自定义分页对象
    }
}
